当代码运行报错时,我们会打印错误,错误中有堆栈信息,可以定位到对应的代码位置。但有的时候我们希望能够更直接准确的打印报错位置的代码。比如这样:

这个可以使用 @babel/code-frames 来做到:
const { codeFrameColumns } = require('@babel/code-frame');
const res = codeFrameColumns(code, {
start: { line: 2, column: 1 },
end: { line: 3, column: 5 },
}, {
highlightCode: true,
message: '这里出错了'
});
console.log(res);
@前端进阶之旅: 代码已经复制到剪贴板
当然,也可以直接使用 path.buildCodeFrameError(path, options) 来创建这种错误信息。
那么它是怎么做到的打印出上面的 code frame 的代码格式的呢?这节我们就来探究下原理。
主要会解答三个问题:
- 如何打印出标记相应位置代码的 code frame(就是上图的打印格式)
- 如何实现语法高亮
- 如何在控制台打印颜色
# 如何打印 code frame
我们先不管高亮,实现这样的格式的打印:

传入了源代码、标记开始和结束的行列号,那么我们就能够计算出显示标记(marker “>”)的行是哪些,以及这些行的哪些列,然后依次对每一行代码做处理,如果本行没有标记则保持原样,如果本行有标记的话,那么就在开始打印一个 marker “>”,并且在下面打印一行 marker "^",最后一个标记行还要打印错误信息。
我们来看一下 @babel/code-frame 的实现:
首先,分割字符串成每一行的数组,然后根据传入的位置计算出 marker(>) 所在的位置。
比如图中第二行的第 1 到 12 列,第三行的 0 到 5 列。

然后对每一行做处理,如果本行有标记,则拼成 marker + gutter(行号) + 代码的格式,下面再打印一行 marker,最后的 marker 行打印 message。没有标记不处理。

这样最终拼出的就是这样的 code frame:

我们实现了 code frame 的拼接,暂时忽略了高亮,那么怎么做语法高亮呢?
# 如何实现语法高亮
实现语法高亮,词法分析就足够了,babel 也是这么做的,@babel/highlight 包里面完成了高亮代码的逻辑。
先看效果:
const a = 1;
const b = 2;
console.log(a + b);
@前端进阶之旅: 代码已经复制到剪贴板
上面的源码被分成了 token 数组:
[
[ 'whitespace', '\n' ], [ 'keyword', 'const' ],
[ 'whitespace', ' ' ], [ 'name', 'a' ],
[ 'whitespace', ' ' ], [ 'punctuator', '=' ],
[ 'whitespace', ' ' ], [ 'number', '1' ],
[ 'p